home *** CD-ROM | disk | FTP | other *** search
- [LISTING 4]
- ;=============================================================================
- ;
- ; Start-up code for embedded C programs
- ;
- ; intended for use with Microsoft C, but porting it to Borland C
- ; or other similar compilers shouldn't be too difficult.
- ;
- ; Donated to the public domain by Jeff D. Pipkins, who of course is
- ; not liable for damages...
- ;
- ;
- ; This file MUST be linked FIRST. The linker will combine segments
- ; with the same class name together, and it will order those combined
- ; segments in the order that they first appear in this file. This
- ; is extremely important. It is always the first file the linker
- ; sees that gets to choose the ordering of the segments.
- ;
- ;-----------------------------------------------------------------------------
-
- ROMSEG EQU 0F800h ; beginning of ROM
- STACKSIZE EQU 4096
-
- ;-----------------------------------------------------------------------------
- ; data group (to fit in 64K)
- ;
- ; For our model, we need the data to appear first in the image,
- ; with the code up above it. Unfortunately, we also need for
- ; execution to begin at a fixed location known at build time,
- ; and exe2bin wants that location to be either at offset 0 or
- ; 100h from the beginning of the image. We solve this problem
- ; by putting a little code in the front of the data segment that
- ; can figure out where to jump.
- ;
- ;-----------------------------------------------------------------------------
-
- DGROUP group NOTSTACK, NULL, CONST, _DATA, _BSS, STACK
- assume cs:DGROUP, ds:DGROUP
- public jumpstart
-
- ; The linker complains if there's no stack, and exe2bin
- ; complains if there is. This segment is here to fool
- ; the linker. See also the STACK segment, where exe2bin
- ; is fooled.
-
- NOTSTACK segment byte stack 'NOTSTACK'
- ; This must remain empty.
- NOTSTACK ends
-
-
- NULL segment para public 'NULL'
-
- ; The following bytes are the first bytes in the image.
- ; MSC wants 16 bytes of 0's here.
- ; We need to begin execution here as well...
-
- jumpstart:
- dw 8 dup (0) ; 8 x add [bx+si], al
-
- ; There's already code in the reset vector to
- ; do a cli & cld, but it's here again just for
- ; emphasis, and more importantly, in case somebody
- ; decides to modify the reset vector in the future.
-
- cli ; interrupts off -- no stack yet, no vectors yet.
- cld ; initialize direction flag
-
- ; Compute segment address of "startup" label
- ; The build process "locates" (provides fix-ups for) the
- ; image so that it will run properly if it is loaded into
- ; a particular address in RAM. This is what we want, since
- ; our data will be copied into RAM. Unfortunately, we
- ; really need for our code segment to be "located" with
- ; respect to the beginning of ROM instead. We resolve
- ; this dilemma in favor of the data, and then we'll set
- ; the value of CS by hand. This way, the code won't
- ; know that it's in the wrong place. This illusion will
- ; all crash if anyone does a far jump, since the value
- ; for CS will have been fixed-up incorrectly! So, no
- ; far jumps anywhere! The only exception to this follows.
- ; We get away with it because we compensate for the
- ; miscalculation in advance. (All of this hokey stuff is
- ; a consequence of using exe2bin for a locator.)
-
- mov dx, _TEXT ; Segment address of code segment
- mov ax, ROMSEG ; Should be same as CS for right now
- add dx, ax ; Add fix-up for code segment
- mov ax, DGROUP ; Segment address where data will be
- sub dx, ax ; undo fix-up for data segment in RAM
-
- ; Temporarily use dword at DGROUP:0 to hold vector for jump.
-
- mov ds, ax ; DGROUP
- xor ax, ax
- mov word ptr ds:[0], ax
- mov word ptr ds:[2], dx ; computed segment for jump
-
- jmp dword ptr ds:[0] ; to "startup"
-
- ; and now back to your regularly scheduled data...
-
- NULL ends
-
- ; Variables declared as "const" will go here.
-
- CONST segment word public 'CONST'
- CONST ends
-
- ; Initialized global and static variables will go here.
-
- _DATA segment word public 'DATA'
- public __acrtused, _errno
- __acrtused dw 0 ; prevents linking in Microsoft's
- _errno dw 0 ; standard start-up code & libs
- _DATA ends
-
- ; Uninitialized global and static variables will go here.
- ; The C language guarantees that variables in this segment
- ; will be initialized to 0 at load time. It is up to the
- ; startup code to make good on this promise. Since we
- ; are using exe2bin, it will fill in segments like this
- ; one with zeros, but in many other environments, this
- ; has to be done explicitly. If you change the build
- ; process so that this becomes necessary, perhaps the
- ; easiest way to deal with it is to just begin by filling
- ; all of RAM with zeros. That way you'll also initialize
- ; the FAR_BSS and HUGE_BSS at the same time, and you won't
- ; have to figure out where any of them begin or end.
-
- _BSS segment word public 'BSS'
- _BSS ends
-
- ; Note: exe2bin doesn't want to see a stack segment because
- ; it knows the loader won't be able to set up the stack as
- ; specified. This startup code will set up this stack for us,
- ; but we need to trick exe2bin so that it won't know this is
- ; a stack. So we define it as PUBLIC instead of STACK.
-
- STACK segment word public 'STACK'
- public __stackend
- db STACKSIZE/8 dup ("<STACK> ")
- __stackend label word
- STACK ends
-
-
- ;-----------------------------------------------------------------------------
- ; data segments that are not part of dgroup
- ;-----------------------------------------------------------------------------
-
- FAR_DATA_START segment para public 'FAR_DATA'
- FAR_DATA_START ENDS
-
- FAR_BSS_START segment para public 'FAR_BSS'
- FAR_BSS_START ends
-
- HUGE_BSS_START segment para public 'HUGE_BSS'
- HUGE_BSS_START ends
-
- ;-----------------------------------------------------------------------------
- ; mark the end of all the data in the image
- ;-----------------------------------------------------------------------------
-
- ENDDATAIMAGE segment para public 'ENDDATAIMAGE'
- public __enddataimage
- __enddataimage label byte
- ENDDATAIMAGE ends
-
-
- ;-----------------------------------------------------------------------------
- ; Tell linker where to put the code segment.
- ;-----------------------------------------------------------------------------
-
- _TEXT segment para public 'CODE'
- _TEXT ends
-
- ;-----------------------------------------------------------------------------
- ; mark the end of the entire image
- ;-----------------------------------------------------------------------------
-
- ENDIMAGE segment para public 'ENDIMAGE'
- public __endimage
- __endimage label byte
- ENDIMAGE ends
-
-
- ;=============================================================================
- ;
- ; Start-up code
- ;
- ;-----------------------------------------------------------------------------
-
- _TEXT segment
- assume CS:_TEXT, DS:DGROUP, SS:DGROUP
-
- extrn _main:near
- public startup
- startup:
- ;----------------------------------------------------------------
- ; Initialize interrupt vectors to point to the restart vector.
- ; This will cause a restart on any unexpected interrupt.
- ;----------------------------------------------------------------
-
- xor ax, ax
- mov ds, ax
- xor bx, bx
- mov cx, 256
- init_int_loop:
- mov [bx+0], 0FFF0h ; offset
- mov [bx+2], 0F000h ; segment
- add bx, 4
- loop init_int_loop
-
- ;----------------------------------------------------------------
- ; Set up a temporary stack
- ;----------------------------------------------------------------
-
- mov ax, DGROUP
- mov ds, ax
- mov bx, offset DGROUP:__stackend
- and bx, NOT 1 ; align sp on word boundary
- mov ss, ax ; DGROUP
- mov sp, bx ; __stackend
-
- ;----------------------------------------------------------------
- ; Copy data image from ROM to RAM
- ;
- ; For later processors with protected mode, this code assumes
- ; that the ROM is reflected down into the top of the first
- ; 1MB of address space.
- ;----------------------------------------------------------------
-
- mov ax, DGROUP
- mov es, ax
- mov dx, seg __enddataimage
- sub dx, ax ; number of paragraphs to copy
- mov ax, ROMSEG
- mov ds, ax
- copyloop:
- xor si, si
- xor di, di
- mov cx, 8
- rep movsw
-
- mov ax, es ;\
- inc ax ; > inc es
- mov es, ax ;/
-
- mov ax, ds ;\
- inc ax ; > inc ds
- mov ds, ax ;/
-
- dec dx
- jnz copyloop
-
- ;----------------------------------------------------------------
- ; setup segment registers
- ;----------------------------------------------------------------
-
- mov ax, DGROUP
- mov ds, ax
- mov es, ax
-
- ;----------------------------------------------------------------
- ; setup C stack and first frame
- ;----------------------------------------------------------------
-
- mov bx, offset DGROUP:__stackend
- and bx, NOT 1 ; align sp on word boundary
- mov ss, ax ; DGROUP
- mov sp, bx ; __stackend
- xor bp, bp ; frame 0
- push bp ; mark it on the stack
- mov bp, sp ; bp always has current frame pointer
-
- ;----------------------------------------------------------------
- ; Call main()
- ; note: CLI, CLD on entry.
- ;----------------------------------------------------------------
-
- call _main
-
- ;----------------------------------------------------------------
- ; For embedded apps, main() should never return.
- ; If for some reason it does, we'll start all over
- ;----------------------------------------------------------------
-
- ; (for some reason, it's difficult to convince
- ; the assembler to generate this instruction.)
- db 0EAh ; jmp far
- dd 0F000FFF0h ; restart vector
-
-
- _TEXT ends
-
- ;=============================================================================
- ;
- ; Set entry point to beginning of image.
- ; (This is done mostly to satisfy the assembler and exe2bin.)
- ;
- ;-----------------------------------------------------------------------------
-
- end jumpstart
-
-
-